/**
 * @file
 * @brief Defines the canbus messages.
 */

#ifndef _WS_CANBUS_MSG_H_
#define _WS_CANBUS_MSG_H_

#include <iostream>
#include <algorithm>
#include <array>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include <chrono>
#include <string>
#include "ros/ros.h"
#include <canlib.h>
#include "math.h"

#include "canbus/byte.h"

using namespace std;

namespace canbus {

/**
 * @class MsgBase
 * @brief The base class of Physical layer message.
 */
class MsgBase {
public:
  /**
   * @brief Constructor default.
   */
  MsgBase() = default;

  /**
   * @brief Destructor default.
   */  
  virtual ~MsgBase() = default;

  virtual void UpUpdate(uint8_t* value) = 0;

  template <typename T>
  void DownUpdate(const T& input_data);

  uint16_t data_length_;
  uint8_t* data_;
  uint32_t id_;
};

/**
 * @class CanMsg
 * @brief The class of can message.
 */
class CanMsg: public MsgBase {
public:
  /**
   * @brief Constructor default that initialize data to zero
   */
  CanMsg();

  /**
   * @brief Constructor which takes a pointer to unsigned integer.
   * @param value The pointer to one-byte unsigned integers for construction.
   */
  CanMsg(uint8_t* value);

  /**
   * @brief Destructor default.
   */  
  virtual ~CanMsg() = default;

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  uint8_t* getBytes() {
    return data_;
  }

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  long getID() const {
    return id_;
  }

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  void SetByte(const uint8_t& data, int digit);

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  void Reset();

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  int32_t ParseTwins(const std::uint8_t low_byte,
                     const std::uint8_t high_byte) const;

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  int32_t ParseQuarter(const std::uint8_t q1,
                       const std::uint8_t q2,
                       const std::uint8_t q3,
                       const std::uint8_t q4) const;

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  uint8_t getTwin(const int32_t& twins, bool is_upper);

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  uint8_t getQuarter(const int32_t& quarters, int position);

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  void UpUpdate(uint8_t* value);

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  virtual void UpUpdateData() = 0;

  /**
   * @brief write vehicle command message to canbus
   * @param vcmd constant pointer points to the messages.
   */
  template <typename T>
  void DownUpdate(const T& input_data);
};


} // namespace
 
#endif // _WS_CANBUS_MSG_H_